package com.xiaomaoguai.fcp.utils.money;

import lombok.experimental.UtilityClass;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 金额处理工具类
 *
 * @author litianwei
 */
@UtilityClass
public class MoneyTool {

	public static final BigDecimal MIN_AMOUNT = new BigDecimal("0.01");

	public static boolean greaterThanZero(BigDecimal amount) {
		return amount != null && amount.compareTo(BigDecimal.ZERO) > 0;
	}

	public static int compare(BigDecimal m1, BigDecimal m2) {
		return null2Zero(m1).compareTo(null2Zero(m2));
	}

	public static BigDecimal max(BigDecimal m1, BigDecimal m2) {
		BigDecimal bigDecimal1 = null2Zero(m1);
		BigDecimal bigDecimal2 = null2Zero(m2);
		return bigDecimal1.compareTo(bigDecimal2) >= 0 ? bigDecimal1 : bigDecimal2;
	}

	public static BigDecimal min(BigDecimal m1, BigDecimal m2) {
		BigDecimal bigDecimal1 = null2Zero(m1);
		BigDecimal bigDecimal2 = null2Zero(m2);
		return bigDecimal1.compareTo(bigDecimal2) < 0 ? bigDecimal1 : bigDecimal2;
	}

	public static BigDecimal null2Zero(BigDecimal amount) {
		return amount == null ? BigDecimal.ZERO : amount;
	}

	/**
	 * 多个金额相加，至少2个参数
	 */
	public static BigDecimal add(BigDecimal amount1, BigDecimal amount2, BigDecimal... amounts) {
		BigDecimal result = null2Zero(amount1).add(null2Zero(amount2));
		for (BigDecimal amount : amounts) {
			result = result.add(null2Zero(amount));
		}
		return result;
	}

	/**
	 * 多个金额相乘，至少2个参数，不处理精度
	 */
	public static BigDecimal multiply(BigDecimal amount1, BigDecimal amount2, BigDecimal... amounts) {
		BigDecimal result = null2Zero(amount1).multiply(null2Zero(amount2));
		for (BigDecimal amount : amounts) {
			result = result.multiply(null2Zero(amount));
		}
		return result;
	}

	/**
	 * 多个金额相乘，至少2个参数，四舍五入保留两位小数
	 */
	public static BigDecimal multiplyWithScale(BigDecimal amount1, BigDecimal amount2, BigDecimal... amounts) {
		BigDecimal result = null2Zero(amount1).multiply(null2Zero(amount2));
		for (BigDecimal amount : amounts) {
			result = result.multiply(null2Zero(amount));
		}
		result = result.setScale(2, RoundingMode.HALF_UP);
		return result;
	}

	public static BigDecimal subtract(BigDecimal amount1, BigDecimal amount2) {
		return null2Zero(amount1).subtract(null2Zero(amount2));
	}

	/**
	 * 对两个字符串做除法（不丢失精度），四舍五入保留指定位数的小数
	 *
	 * @param src
	 * @param dest
	 * @param scale 保留位数
	 */
	public static BigDecimal divide(BigDecimal src, BigDecimal dest, int scale) {
		BigDecimal srcDeci = null2Zero(src);
		BigDecimal destDeci = null2Zero(dest);
		return srcDeci.divide(destDeci, scale, RoundingMode.HALF_UP);
	}

	/**
	 * 元转分,去掉小数点,为收单使用
	 */
	public static BigDecimal yuan2Fen(BigDecimal amount) {
		if (amount == null) {
			return BigDecimal.ZERO;
		}
		return amount.movePointRight(2).setScale(0, BigDecimal.ROUND_DOWN);
	}

	/**
	 * 检查是否是正确的资金金额
	 *
	 * @param val
	 * @return
	 */
	public static boolean checkValidAmount(BigDecimal val) {
		if (null == val) {
			return true;
		}
		BigDecimal result = new BigDecimal(val.toString()).setScale(2, RoundingMode.DOWN);
		return result.compareTo(val) == 0;
	}

	/**
	 * NPV公式，用于计算现值PV，保留2位小数，四舍五入
	 *
	 * @param perNCF     每期的净现金流(除最后一期外)
	 * @param perRate    收益率
	 * @param periods    期数
	 * @param lastPerNCF 最后一期的净现金流
	 * @return
	 */
	public static BigDecimal calPv(BigDecimal perNCF, BigDecimal perRate, int periods, BigDecimal lastPerNCF) {
		BigDecimal result = BigDecimal.ZERO;
		for (int i = 1; i <= periods - 1; i++) {
			result = result.add(perNCF.divide(perRate.add(BigDecimal.ONE).pow(i), 4, RoundingMode.DOWN));
		}
		result = result.add(lastPerNCF.divide(perRate.add(BigDecimal.ONE).pow(periods), 4, RoundingMode.DOWN));
		result = result.divide(new BigDecimal(1), 6, RoundingMode.HALF_UP);
		return result;
	}

	/**
	 * PMT公式，用于计算每期净现金流NCF，保留2位小数，四舍五入
	 *
	 * @param firstFlow 期初现金流出
	 * @param perRate   收益率
	 * @param periods   期数
	 * @return
	 */
	public static BigDecimal calEachNcf(BigDecimal firstFlow, BigDecimal perRate, int periods) {
		BigDecimal divisor = firstFlow.multiply(perRate.add(BigDecimal.ONE).pow(periods));
		BigDecimal dividend = BigDecimal.ZERO;
		BigDecimal result = BigDecimal.ZERO;
		for (int i = 0; i < periods; i++) {
			dividend = dividend.add(perRate.add(BigDecimal.ONE).pow(i));
		}
		result = divisor.divide(dividend, 6, RoundingMode.HALF_UP);
		return result;
	}

	/**
	 * 计算第N期的终值（前提：前几期每期的净现金流必须相等）,保留2位小数，四舍五入
	 *
	 * @param PV         未来收益的现值
	 * @param preEachNcf 前几期每期的净现金流
	 * @param perRate    收益率
	 * @param periods    当前的期数
	 * @return
	 */
	public static BigDecimal calFvByPV(BigDecimal PV, BigDecimal preEachNcf, BigDecimal perRate, int periods) {
		BigDecimal result = PV.multiply(perRate.add(BigDecimal.ONE).pow(periods));
		for (int i = 1; i < periods; i++) {
			result = result.subtract(preEachNcf.multiply(perRate.add(BigDecimal.ONE).pow(i)));
		}
		result = result.divide(new BigDecimal(1), 6, RoundingMode.HALF_UP);
		return result;
	}

	/**
	 * 检查分期金额是否合法
	 *
	 * @param arg0
	 * @param arg1
	 * @return
	 */
	public static boolean checkPeriodAmount(BigDecimal arg0, Integer arg1) {
		if (arg1 > 0) {
			BigDecimal periodAmount = divide(arg0, new BigDecimal(arg1), 2);
			return (periodAmount.compareTo(BigDecimal.ZERO) > 0);
		}
		return true;
	}

	/**
	 * 金额精度检查
	 *
	 * @param val
	 * @param size
	 * @return
	 */
	public static boolean checkAmount(BigDecimal val, int size) {
		if (val == null || val.toString().split("\\.").length <= 1) {
			return false;
		}
		return val.toString().split("\\.")[1].length() <= size;
	}
}
